package com.teamone.mobilewatch.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.TreeMap;

import org.json.JSONArray;
import org.json.JSONException;

import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;

import com.teamone.mobilewatch.accessor.webservice.PatientWatchDataAccessorImpl;

/**
 * Keeps track of patients to be monitored in the watchlist and related tasks 
 *
 */
public class Watchlist {
	
	private static final String TAG = "Watchlist";	
	public static final String PREFERENCES_FILE = "watchlist";
	public static final String PREFERENCES_ITEM_KEY = "JSONArray";
	private TreeMap<Integer, Patient> mPatients;
	
	private static Watchlist watchlist;
	private int selectedId;

	/**
	 * Creates new instance
	 */
	private Watchlist() {
		mPatients = new TreeMap<Integer, Patient>();
	}
	
	/**
	 *	Returns instance of the class 
	 */
	public static Watchlist getInstance(){
		if (watchlist == null) {
			watchlist = new Watchlist();
		}
		
		return watchlist;
	}

	/**
	 * Adds patient to the watchlist
	 * 
	 * @param patientId - patient id
	 * @param patient - Patient object
	 */
	public void add(int patientId, Patient patient) {
		Log.d(TAG,"adding patientId=" + patientId);
		mPatients.put(patientId, patient);
	}
	/**
	 * Removes patient from the watchlist
	 * 
	 * @param patientId - patient id
	 * @return Patient object removed 
	 */
	public Patient remove(int patientId) {
		Log.d(TAG,"removing patientId=" + patientId);
		return mPatients.remove(patientId); 
	}

	/**
	 * Checks if watchlist contains patientId
	 * @param patientId - patient id
	 * @return <true> if contains the patient id
	 */
	public boolean containsId(int patientId) {
		return mPatients.containsKey(patientId);
	}
	
	/**
	 * Checks if the watchlist contains cached Patient object for patientId 
	 * @param patientId - patient id
	 * @return <true> if contains the Patient object
	 */
	public boolean containsPatient(int patientId) {
		boolean containsPatient = false;
		Patient patient = mPatients.get(patientId);
		if(patient != null) {
			containsPatient = true;
		}

		return containsPatient;
	}

	/**
	 * Clears watchlist
	 */
	public void clear() {
		mPatients.clear();
	}


	/**
	 * Gets Patient from data source and saves it to watchlist
	 * @param patientId - patient id
	 * @return synced Patient object
	 */
	private Patient syncUp(int patientId) {
		Patient syncedPatient = null;
		
		ArrayList<Patient> patients;
		try {
			patients = PatientWatchDataAccessorImpl.getInstance().getPatient(patientId);
			if (!patients.isEmpty()) {
				Log.d(TAG, "syncing up patientId="+ patientId);
				syncedPatient = patients.get(0); 
				add(patientId, syncedPatient);
			}
		} catch (Exception e) {
			Log.e(TAG, e.getMessage());
		}
		
		return syncedPatient;
	}

	/**
	 * Gets Patient objects from datasource for all patientIds in the watchlist and saves it to watchlist
	 * @param force - if true - sync of all patients (cached/non-cached), if false - sync non-cahched
	 */
	private void syncUpAll(boolean force) {
		
		Set<Integer> keys = mPatients.keySet();
		for (Integer patientId : keys) {
			if(force || !containsPatient(patientId)) {
				syncUp(patientId);
			}
		}
	}
	
	
	/**
	 * Load from persistence store if any
	 */
	public void loadFromPersistence(Context context) {
		
		SharedPreferences appPreferences = context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
		String jsonString = null;  
		if (appPreferences.contains(PREFERENCES_ITEM_KEY)){
			Log.d(TAG,"loading form SharedPreferences...");
			jsonString = appPreferences.getString(PREFERENCES_ITEM_KEY, "[]");
			deserializeFromJSONArray(jsonString);
			//syncUpAll(true); //?? 
		} else {
			Log.d(TAG,"No data in SharedPreferences");
		}
	}

	/**
	 * Save to persistence store
	 */
	public void saveToPersistence(Context context) {
		SharedPreferences appPreferences = context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
		String jsonString = serializeToJSONArray();  
		Log.d(TAG,"saving to SharedPreferences...");
		SharedPreferences.Editor  editor = appPreferences.edit();
		editor.putString(PREFERENCES_ITEM_KEY, jsonString);
		editor.commit();
	}

	/**
	 * Returns JSON Array of patient IDs 
	 * @return
	 */
	private String serializeToJSONArray() {
		String jsonString = null;
		Set<Integer>ids = mPatients.keySet();
		try {
			JSONArray jsonArray = new JSONArray(ids);
			jsonString = jsonArray.toString();
			Log.d(TAG, "serialized to JSONArray:" + jsonString );
		} catch (Exception e) {
			Log.e(TAG, e.getMessage());
		}

		return jsonString;

	}

	/**
	 * Deserialize JSONArray of patient IDs to watchlist
	 * @param jsonString
	 */
	private void deserializeFromJSONArray(String jsonString) {
		try {
			mPatients.clear();
			JSONArray jsonArray = new JSONArray(jsonString);
			for (int i = 0; i < jsonArray.length(); i++) {
				try {
					mPatients.put(jsonArray.getInt(i), null); // put null, synch-up later
				} catch (JSONException e) {
					Log.e(TAG, e.getMessage());
				}
			}

			Log.d(TAG, "deserialized:" + toString());
		} catch (Exception e) {
			Log.e(TAG, e.getMessage());
		}
	}
	
	public void setSelectedId(int patientId) {
		Log.d(TAG,"selected patientId=" + patientId);
		selectedId = patientId;
	}
	public Patient getSelected() {
		Log.d(TAG,"retrieved selected patientId=" + selectedId);
		return mPatients.get(selectedId);
	}
	

	/**
	 * Converts watchlist into JSONArray string
	 * @return
	 */
	public String toString(){
		// TODO: Patient
		return serializeToJSONArray();
	}
	
	/**
	 * Converts watchlist into array of integers
	 * @return
	 */
	public Integer[] toIntArray(){
		
		Set<Integer>ids = mPatients.keySet();
		Integer[] idsIntArray = new Integer[ids.size()];
		ids.toArray(idsIntArray);
		
		return idsIntArray;
	}
	/**
	 * Returns patients in the watchlist as ArrayList
	 * 
	 */
	public ArrayList<Patient> getPatients() {
		// sync-up non-chached
		syncUpAll(false);
		Collection<Patient> patients= mPatients.values();
		return new ArrayList<Patient>(patients);
	}

	
	/**
	 * Returns patient form the watchlist
	 * @param patientId - patient id
	 * @return Patient object
	 */
	public Patient getPatient(int patientId) {
		
		Patient patient = mPatients.get(patientId);
		if (patient == null) { // sync-up required
			patient = syncUp(patientId);
		}
		return patient;
	}
	
}
